package com.atguigu.tingshu.album.service.impl;

import cn.hutool.core.util.IdUtil;
import com.atguigu.tingshu.album.service.TestService;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.concurrent.TimeUnit;

/**
 * @author: atguigu
 * @create: 2023-10-07 08:54
 */
@Slf4j
@Service
public class TestServiceImpl implements TestService {

    @Autowired
    private StringRedisTemplate redisTemplate;


    /*@Override
    public void testLock() {
        try {
            //1.从Redis缓存中获取key="num"的值  保证redis中存在"num"(手动提前在redis中创建key)
            String value = stringRedisTemplate.opsForValue().get("num");
            if (StringUtils.isBlank(value)) {
                return;
            }
            //2.对获取到值进行+1操作
            int num = Integer.parseInt(value);
            stringRedisTemplate.opsForValue().set("num", String.valueOf(++num));

        } catch (NumberFormatException e) {
            e.printStackTrace();
        }
    }*/


    private static final String lockKey = "lock";
    private static final String lockValue = "lock";


    /**
     * 自定义分布式锁
     */
    public void testLock1() {
        //一、先获取分布式锁
        String lockVal = IdUtil.randomUUID();
        Boolean flag = redisTemplate.opsForValue().setIfAbsent(lockKey, lockVal, 5, TimeUnit.SECONDS);
        if (flag) {
            //二、获取锁成功执行业务代码
            try {
                //1.从Redis缓存中获取key="num"的值  保证redis中存在"num"(手动提前在redis中创建key)
                String value = redisTemplate.opsForValue().get("num");
                if (StringUtils.isBlank(value)) {
                    return;
                }
                //2.对获取到值进行+1操作
                int num = Integer.parseInt(value);
                redisTemplate.opsForValue().set("num", String.valueOf(++num));

            } catch (NumberFormatException e) {
                e.printStackTrace();
            } finally {
                //三、释放锁
                //if(lockVal.equals(redisTemplate.opsForValue().get(lockKey))){
                //    redisTemplate.delete(lockKey);
                //}
                String scriptText = "if redis.call(\"get\",KEYS[1]) == ARGV[1]\n" +
                        "then\n" +
                        "    return redis.call(\"del\",KEYS[1])\n" +
                        "else\n" +
                        "    return 0\n" +
                        "end";
                DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
                redisScript.setScriptText(scriptText);
                redisScript.setResultType(Long.class);
                redisTemplate.execute(redisScript, Arrays.asList(lockKey), lockVal);
            }
        } else {
            //本次线程获取锁失败-业务要求必须执行（自旋）
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            this.testLock();
        }
    }


    @Autowired
    private RedissonClient redissonClient;


    /**
     * 采用Redisson提供分布式
     */
    @Override
    public void testLock() {
        //一、先获取分布式锁
        //  创建锁对象
        RLock myLock = redissonClient.getLock("myLock");
        //  获取锁
        myLock.lock();

        //二、执行业务代码
        try {
            //1.从Redis缓存中获取key="num"的值  保证redis中存在"num"(手动提前在redis中创建key)
            String value = redisTemplate.opsForValue().get("num");
            if (StringUtils.isBlank(value)) {
                return;
            }
            //2.对获取到值进行+1操作
            int num = Integer.parseInt(value);
            redisTemplate.opsForValue().set("num", String.valueOf(++num));

        } catch (NumberFormatException e) {
            e.printStackTrace();
        } finally {
            //三、释放锁
            myLock.unlock();
        }
    }


    /**
     * 读数据使用读锁
     */
    @Override
    public void read() {
        //1.创建读写锁对象
        RReadWriteLock rwlock = redissonClient.getReadWriteLock("myLock");
        //2.获取读锁对象
        RLock rLock = rwlock.readLock();
        //3.加读锁
        rLock.lock(5, TimeUnit.SECONDS);
        System.out.println("模拟正在进行读数据..");
        //4.释放读锁
        //rLock.unlock();  //5秒后自动释放读锁，期间并发读允许 ， 并发写不允许
    }

    /**
     * 写数据使用写锁
     */
    @Override
    public void write() {
        //1.创建读写锁对象
        RReadWriteLock rwlock = redissonClient.getReadWriteLock("myLock");
        //2.获取写锁对象
        RLock wLock = rwlock.writeLock();
        //3.获取写锁
        wLock.lock(5, TimeUnit.SECONDS);
        System.out.println("模拟正在写入数据..");
        //4.释放写锁
        //wLock.unlock();  //5秒后自动释放读锁，期间并发读写都不允许
    }


    //@Autowired
    //private RedissonClient redissonClient;

    /**
     * 发送延迟消息
     *
     * @param ttl
     * @param msg
     */
    @Override
    public void sendDelyMsg(int ttl, String msg) {
        //1.基于RedissonClient创建阻塞队列
        RBlockingQueue<String> blockingQueue = redissonClient.getBlockingQueue("myBlockingQueue");
        //2.基于RedissonClient将阻塞队列作为参数场景延迟队列
        RDelayedQueue<String> delayedQueue = redissonClient.getDelayedQueue(blockingQueue);
        //3.调用延迟队列发送延迟消息
        delayedQueue.offer(msg, ttl, TimeUnit.SECONDS);
        log.info("延迟消息：{}，发送成功,延迟：{}秒", msg, ttl);
    }


    /*
    项目启动后开启一个线程负责监听延迟队列中消息
    * */
    @PostConstruct
    public void receiverDelayMsg() {
        log.info("开始监听");
        RBlockingQueue<String> blockingQueue = redissonClient.getBlockingQueue("myBlockingQueue");
        new Thread(() -> {
            while (true){
                String poll = blockingQueue.poll();
                if (StringUtils.isNotBlank(poll)) {
                    log.info("监听到延迟消息：{}", poll);
                }
            }
        }).start();
    }
}
